package com.amos.knife4j.config.repetition;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.amos.knife4j.config.constants.Constants;
import com.amos.knife4j.config.result.ResultEnums;
import com.amos.knife4j.config.result.ResultUtil;
import com.amos.knife4j.manager.RedisManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * @Title: 防止用户重复提交数据拦截器
 * @Description: 将用户访问的url和参数结合token存入redis，每次访问进行验证是否重复请求接口
 * @Auther: xhq
 * @Version: 1.0
 * @create 2019/3/26 10:35
 */
@Component
public class RepetitionSubmitInterceptor extends HandlerInterceptorAdapter {

    private static Logger LOG = LoggerFactory.getLogger(RepetitionSubmitInterceptor.class);

    /**
     * 通过构造函数的方式获取注入的service
     */
    private final RedisManager redisManager;

    public RepetitionSubmitInterceptor(RedisManager redisManager) {
        this.redisManager = redisManager;
    }

    private final String REPETION_LOCK = "REPETION_LOCK:";

    /**
     * 是否阻止提交,fasle阻止,true放行
     *
     * @return
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (handler instanceof HandlerMethod) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            RepetitionSubmit annotation = method.getAnnotation(RepetitionSubmit.class);
            if (annotation != null) {
                if (repeatDataValidator(request)) {
                    //请求数据相同
                    LOG.warn("please don't repeat submit,url:" + request.getServletPath());
                    response.getWriter().write(JSON.toJSONString(ResultUtil.error(ResultEnums.FAILED.getCode(),"请勿重复提交")));
                    response.getWriter().close();
                    return false;
                } else {//如果不是重复相同数据
                    return true;
                }
            }
            return true;
        } else {
            return super.preHandle(request, response, handler);
        }
    }

    /**
     * 验证同一个url数据是否相同提交,相同返回true
     *
     * @param httpServletRequest
     * @return
     */
    public boolean repeatDataValidator(HttpServletRequest httpServletRequest) {
        //获取请求参数map
        Map<String, String[]> parameterMap = httpServletRequest.getParameterMap();
        Iterator<Map.Entry<String, String[]>> it = parameterMap.entrySet().iterator();

        Map<String, String[]> parameterMapNew = new HashMap<>();
        while (it.hasNext()) {
            Map.Entry<String, String[]> entry = it.next();
            if (!entry.getKey().equals("timeStamp") && !entry.getKey().equals("sign")) {
                //去除sign和timeStamp这两个参数，因为这两个参数一直在变化
                parameterMapNew.put(entry.getKey(), entry.getValue());
            }
        }
        String token = "default";
        if (httpServletRequest.getContextPath().equals(Constants.APP_CONTEXT)) {
            token = httpServletRequest.getHeader(Constants.TOKEN_KEY);
            if (StrUtil.isNullOrUndefined(token)||token.equals("default")) {
                token = httpServletRequest.getParameter(Constants.TOKEN_KEY);
            }
        } else {
            Cookie cookie = ServletUtil.getCookie(httpServletRequest, Constants.TOKEN_KEY);
            if (!ObjectUtil.isNull(cookie)) {
                token = cookie.getValue();
            }
        }
        //过滤过后的请求内容
        String params = JSONObject.toJSONString(parameterMapNew);
        String url = httpServletRequest.getRequestURI();
        Map<String, String> map = new HashMap<>();
        //key为接口，value为参数
        map.put(url, params);
        String nowUrlParams = map.toString();

        //StringRedisTemplate smsRedisTemplate = StrUtil.getBean(StringRedisTemplate.class);
        String redisKey = token + url;
        String value = redisManager.get(REPETION_LOCK, redisKey);
        if (value == null) {
            //如果上一个数据为null,表示还没有访问页面
            //存放并且设置有效期，2秒
            redisManager.set(REPETION_LOCK, redisKey, nowUrlParams, 3);
            return false;
        } else {//否则，已经访问过页面
            if (value.equals(nowUrlParams)) {
                //如果上次url+数据和本次url+数据相同，则表示重复添加数据
                return true;
            } else {//如果上次 url+数据 和本次url加数据不同，则不是重复提交
                redisManager.set(REPETION_LOCK, redisKey, nowUrlParams, 3);
                return false;
            }
        }
    }
}